const std = @import("std");
const util = @import("util");
// 单独测试时使用
// const util = @import("util.zig");
const Allocator = std.mem.Allocator;
const logger = std.log;
const xcb = util.xcb;

const Self = @This();
const WmErr = util.WmErr;
const Pairs = struct {
    key: xcb.xcb_window_t,
    value: xcb.xcb_window_t,
};

pub const WinList = struct {
    prev: ?*WinList = null,
    next: ?*WinList = null,
    data: Pairs,
};

var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;

const allocator = gpa.allocator();

head: ?*WinList,
tail: ?*WinList,
len: usize,

/// 初始化
pub fn init() Self {
    return .{
        .head = null,
        .tail = null,
        .len = 0,
    };
}

/// 判断队列是否为空
pub fn empty(self: *Self) bool {
    return self.len == 0;
}
///在队列尾添加节点
pub fn append(self: *Self, key: xcb.xcb_window_t, value: xcb.xcb_window_t) WmErr!void {
    var node = allocator.create(WinList) catch {
        return error.AllocatedMemoryFail;
    };
    node.data = .{ .key = key, .value = value };

    if (self.tail) |v| {
        if (self.contains(key)) return error.ExistKey;
        v.next = node;
        node.prev = v;
        node.next = null;
        self.tail = node;
        self.len += 1;
    } else {
        self.head = node;
        self.tail = node;
        node.next = null;
        node.prev = null;
        self.len = 1;
    }
}
///在队列头添加节点
pub fn prepend(self: *Self, key: xcb.xcb_window_t, value: xcb.xcb_window_t) WmErr!void {
    var node = allocator.create(WinList) catch {
        return error.AllocatedMemoryFail;
    };
    node.data = .{ .key = key, .value = value };
    if (self.head) |v| {
        if (self.contains(key)) return error.ExistKey;
        node.next = v;
        v.prev = node;
        node.prev = null;
        self.len += 1;
    } else {
        self.head = node;
        self.tail = node;
        node.next = null;
        node.prev = null;
        self.len = 1;
    }
}

/// 判断是否存在数据为key的节点
pub fn contains(self: *Self, key: xcb.xcb_window_t) bool {
    var it = self.head;
    return while (it) |node| : (it = node.next) {
        if (node.data.key == key) break true;
    } else false;
}

/// 判断是否存在数据为value的节点
pub fn containsValue(self: *Self, value: xcb.xcb_window_t) bool {
    var it = self.head;
    return while (it) |node| : (it = node.next) {
        if (node.data.value == value) break true;
    } else false;
}

/// 统计数据为key的节点个数
pub fn count(self: *Self) usize {
    return self.len;
}

/// 根据key获取value
pub fn get(self: *Self, key: xcb.xcb_window_t) ?xcb.xcb_window_t {
    var it = self.head;
    return while (it) |node| : (it = node.next) {
        if (node.data.key == key) break node.data.value;
    } else null;
}

/// 打印队列
pub fn printList(self: *Self) void {
    var it = self.head;
    var idx: i32 = 0;
    while (it) |node| : (it = node.next) {
        logger.info("({}, {})[{}] ", .{
            node.data.key,
            node.data.value,
            idx,
        });
        idx += 1;
    }
    logger.info("\n", .{});
}

/// 移除键为key的节点
pub fn remove(self: *Self, key: xcb.xcb_window_t) void {
    var it = self.head;
    while (it) |node| : (it = node.next) {
        if (node.data.key == key) {
            var cur = node;
            if (cur.next == null) {
                self.tail = cur.prev;
            }
            if (cur.prev == null) {
                self.head = cur.next;
            }
            node.prev = cur.next;
            cur.next = node.prev;
            allocator.destroy(cur);
            self.len -= 1;
            break;
        }
    }
}

/// 销毁队列
pub fn deinit(self: *Self) void {
    while (self.head) |node| {
        self.head = node.next;
        var cur = node;
        node.prev = cur.next;
        cur.next = node.prev;
        allocator.destroy(cur);
    }
    self.tail = null;
    self.len = 0;
    defer {
        // 尝试进行 deinit 操作
        const deinit_status = gpa.deinit();

        // 检测是否发生内存泄漏
        if (deinit_status == .leak) @panic("Memory leaks");
    }
}

test "empty" {
    var l = Self.init();
    try std.testing.expect(l.empty());
}

test "append" {
    var l = Self.init();
    try l.append(2, 3);
    try l.append(3, 4);
    try l.append(4, 5);
    try l.append(5, 6);
    var it = l.head;
    var idx: u32 = 2;
    while (it) |node| : (it = node.next) {
        try std.testing.expectEqual(node.data.key, idx);
        try std.testing.expectEqual(node.data.value, idx + 1);
        idx += 1;
    }
}

test "prepend" {
    var l = Self.init();
    try l.prepend(2, 3);
    try l.prepend(3, 4);
    try l.prepend(4, 5);
    try l.prepend(5, 6);
    var it = l.tail;
    var idx: u32 = 2;
    while (it) |node| : (it = node.prev) {
        try std.testing.expectEqual(node.data.key, idx);
        try std.testing.expectEqual(node.data.value, idx + 1);
        idx += 1;
    }
}

test "isExistKey" {
    var l = Self.init();
    try l.append(2, 3);
    try l.append(5, 3);
    try std.testing.expect(l.contains(5));
}

test "count" {
    var l = Self.init();
    try std.testing.expectEqual(l.count(), 0);
    try l.append(2, 3);
    try std.testing.expectEqual(l.count(), 1);
    try l.append(5, 3);
    try std.testing.expectEqual(l.count(), 2);
}

test "remove" {
    var l = Self.init();
    try l.append(1, 2);
    try l.append(2, 3);
    l.remove(1);
    try std.testing.expectEqual(l.tail.?.data.key, 2);
}

test "get" {
    var l = Self.init();
    try l.append(1, 2);
    try l.append(2, 3);
    try std.testing.expectEqual(l.get(2), 3);
}

test "destroy" {
    var l = Self.init();
    try l.append(1, 2);
    try l.append(2, 3);
    try l.append(3, 2);
    try l.append(4, 3);
    try l.append(5, 2);
    try l.append(6, 3);
    l.deinit();

    try std.testing.expectEqual(l.head, null);
}
